#include "TerrainPatch.h"

//We create our grid through the geometry generator. 
#include "GeometryGenerator.h"

//Include the shader manager - allowing us to get the effects we need
#include "ShaderManager.h"

//Used when loading png height maps.
#include "loadpng.h"

//Used when we need to convert two 8 bit uchars to a single 16 bit uint. 
#include <bitset>

//Adds two XMFLOAT3s together
XMFLOAT3 XMFLOAT3Add(XMFLOAT3 &a, XMFLOAT3 &b)
{
	XMFLOAT3 ret;
	ret.x = a.x + b.x;
	ret.y = a.y + b.y;
	ret.z = a.z + b.z;
	return ret;
}

TerrainPatch::TerrainPatch()
{
	//Init D3D resources to NULL. 
	terrainLayerVB = NULL;
	terrainLayerIB = NULL;
	terrainLayerMedVB = NULL;
	terrainLayerMedIB = NULL;

	//Init so we can assert and throw an error if this
	//is not set when using RAW files. 
	terrainWidth = 0.0f;
	terrainDepth = 0.0f;

	//Init other pointers to NULL
	terrainTexData = 0;
}

TerrainPatch::~TerrainPatch()
{
	//Release D3D resources
	ReleaseCOM(terrainLayerVB);
	ReleaseCOM(terrainLayerIB);
	ReleaseCOM(terrainLayerMedVB);
	ReleaseCOM(terrainLayerMedIB);

	//DONT delete - terrain will clean up (hopefully)
	terrainTexData = 0;
}

void TerrainPatch::Init(ID3D11Device* device, 
	const char* heightMapFile, 
	bool isPNG, 
	float terrainCellSize, 
	TerrainPatchTextureData *texData,
	float heightDivFactor,
	bool isSlopeBasedTerrain,
	float height1, float height2)
{
	if (!isInit)
	{
		//Store cell size. 
		this->terrainCellSize = terrainCellSize;

		//Store the texture data
		terrainTexData = texData;

		//Store what rendering type we are using
		useSlopeBasedRendering = isSlopeBasedTerrain;
		//Store height factros
		heightFac1 = height1;
		heightFac2 = height2;

		//Init material - can modify later. 
		terrainMaterial.Ambient  = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);
		terrainMaterial.Diffuse  = XMFLOAT4(0.9f, 0.9f, 0.9f, 1.0f); 
		terrainMaterial.Specular = XMFLOAT4(0.0f, 0.0f, 0.0f, 28.0f);
		terrainMaterial.Reflection  = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);

		//Load the heightmap - before creating the grid and then smooth.  
		std::vector<float>heights;
		if (isPNG)
			LoadHeightMapPNG(device, heights, heightMapFile);
		else
			LoadHeightMap(device, heights, heightMapFile);
	
		//Scale down (or indeed, up...) 
		ScaleHeightMap(heights, heightDivFactor);

		//Create the displaced grid. 
		CreateGrid(device, heights, 0, 0, 0, 0);

		//Clear the heights vector since we dont need it anymore. 
		heights.clear();

		//Init tex transformation matricies, 
		XMStoreFloat4x4(&terrainTexTrans, XMMatrixScaling(terrainWidth/TERRAIN_PATCH_TEX_TRANS_DIV, 
			terrainDepth/TERRAIN_PATCH_TEX_TRANS_DIV, 1.0f));
		XMStoreFloat4x4(&terrainTexDetailTrans, XMMatrixScaling(terrainWidth/TERRAIN_PATCH_DETAIL_TEX_TRANS_DIV, 
			terrainDepth/TERRAIN_PATCH_DETAIL_TEX_TRANS_DIV, 1.0f));

		//Once done, Set is init flag to true. 
		isInit = true;

		//Create AABB.
		GenerateAABB();
	}
	else
		OutputDebugStringW(L"Terrain Warning - IsInit == true already\n");
}

void TerrainPatch::InitAdvanced(ID3D11Device* device, 
		std::vector<float>&heights,   
		std::vector<float>&medHeights,
		float width, float depth,                 
		float terrainCellSize,                  
		TerrainPatchTextureData *texData,
		float texStepSizeX, float texStepSizeY,				
		float minLayerTexCoordX, float minLayerTexCoordY,
		bool isSlopeBasedTerrain,
		float height1, float height2)				 
{
	if(!isInit)
	{
		//Store cell size. 
		this->terrainCellSize = terrainCellSize;
		//And dimentions
		terrainWidth = width;
		terrainDepth = depth;
		
		//Stroe texture data
		terrainTexData = texData;

		//Store what rendering type we are using
		useSlopeBasedRendering = isSlopeBasedTerrain;
		//Store height factros
		heightFac1 = height1;
		heightFac2 = height2;

		//Init materail - can modify later. 
		terrainMaterial.Ambient  = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);
		terrainMaterial.Diffuse  = XMFLOAT4(0.6f, 0.6f, 0.6f, 1.0f); 
		terrainMaterial.Specular = XMFLOAT4(0.3f, 0.3f, 0.3f, 8.0f);
		terrainMaterial.Reflection  = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
		
		//Create the displaced grid. 
		CreateGrid(device, heights, 
			minLayerTexCoordX, minLayerTexCoordY, texStepSizeX, texStepSizeY);

		//Generate medium quality version of the patch
#ifdef TERRAIN_PATCH_GEN_MED_QUALITY
		GenerateMedQualityTerrain(device, medHeights);
#endif

		//Init tex transformation matricies, 
		XMStoreFloat4x4(&terrainTexTrans, XMMatrixScaling(ceilf(terrainWidth/TERRAIN_PATCH_TEX_TRANS_DIV),
			ceilf(terrainDepth/TERRAIN_PATCH_TEX_TRANS_DIV), 1.0f));
		XMStoreFloat4x4(&terrainTexDetailTrans, XMMatrixScaling(ceilf(terrainWidth/TERRAIN_PATCH_DETAIL_TEX_TRANS_DIV),
			ceilf(terrainDepth/TERRAIN_PATCH_DETAIL_TEX_TRANS_DIV), 1.0f));

		//Once done, Set is init flag to true. 
		isInit = true;

		//Create AABB.
		GenerateAABB();
	}
	else
		OutputDebugStringW(L"Terrain Warning - IsInit == true already\n");
}

void TerrainPatch::LoadHeightMap(ID3D11Device* device, std::vector<float>&heightsOut, 
		const char* filename)
{
	//Variables that we use throghout the height map loading stage.
	FILE* filePtr;
	int error;
	unsigned int count;
	BITMAPFILEHEADER bitmapFileHeader;
	BITMAPINFOHEADER bitmapInfoHeader;
	int imageSize, i, k;
	unsigned char* bitmapImage;


	//Open the height map file in binary.
	error = fopen_s(&filePtr, filename, "rb");
	if(error != 0)
	{
		OutputDebugStringW(L"Terrain Error - Unable to open heightmap.\n");
		while(1){}
	}

	// Read in the file header.
	count = fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
	if(count != 1)
	{
		OutputDebugStringW(L"Terrain Error - file header fail\n");
		while(1){}
	}

	// Read in the bitmap info header.
	count = fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);
	if(count != 1)
	{
		OutputDebugStringW(L"Terain Erorr - Bitmap info header failed\n");
		while(1){}
	}

	//Store dimentions
	terrainWidth = (float)bitmapInfoHeader.biWidth;
	terrainDepth = (float)bitmapInfoHeader.biHeight;

	// Calculate the size of the bitmap image data.
	imageSize = (int)terrainWidth * (int)terrainDepth * 3;

	// Allocate memory for the bitmap image data.
	bitmapImage = new unsigned char[imageSize];
	if(!bitmapImage)
	{
		OutputDebugStringW(L"Terrain Error - Failed to alloc memory for bitmapImage\n");
		while(1){}
	}

	// Move to the beginning of the bitmap data.
	fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);

	// Read in the bitmap image data.
	count = fread(bitmapImage, 1, imageSize, filePtr);
	if(count != imageSize)
	{
		OutputDebugStringW(L"Terrain Error - failed to read bitmap image data\n");
		while(1){}
	}

	// Close the file.
	error = fclose(filePtr);
	if(error != 0)
	{
		OutputDebugStringW(L"Terrain Error - Failed to close heightmap file\n");
		while(1){}
	}

	// Initialize the position in the image data buffer - sample every third value
	//from the bitmap image
	k=0;
	for(i=0; i<terrainWidth*terrainDepth; i++)
	{
		//Store height in output vector
		heightsOut.push_back(bitmapImage[k]);
		//Next location to sample within the bitmap image data. 
		k+=3;
	}

	// Release the bitmap image data.
	delete [] bitmapImage;
	bitmapImage = 0;
}

void TerrainPatch::LoadHeightMapRAW8(ID3D11Device* device, std::vector<float>&heightsOut, 
		const char* filename)
{
	//Ensure we have set something reasonable...
	assert(terrainWidth != 0.0f);
	assert(terrainDepth != 0.0f);
	
	// A height for each vertex
	std::vector<unsigned char>in((UINT)terrainWidth * (UINT)terrainDepth);

	//Check size of the file - we compare it to how much data we are expecting in 
	//order to displace each vertex. 
	std::streampos fsize = 0;
	//Open file
    std::ifstream file(filename, std::ios::binary);

	//Location of the first character.
    fsize = file.tellg();
	//Seek to the end. 
    file.seekg( 0, std::ios::end );
	//Difference between the two is the size of the file. 
    fsize = file.tellg() - fsize;
	//Close.
    file.close();

	//Work out what the file size we expect based on our passed in
	//dimentions. 
	long long expSize = (long long)(terrainWidth * terrainDepth * 1.0f); //8bit = 1 byte

	//If the file is bigger than the amount of data we expect (or smaller), then
	//we have the wrong dimentions. Print error.
	if (expSize != fsize)
	{
		OutputDebugStringW(L"Terrain Error - Height map raw file and grid dimentions missmatch\n");
		OutputDebugStringW(L"This is likely an issue with the terrain size being passed in to the Init()\n");
		OutputDebugStringW(L"Function. Terragen seems to insist on adding 1 to width and depth of their textures...\n");
		std::stringstream ss, ss2, ss3;
		ss << fsize;
		ss2 << expSize;
		OutputDebugStringW(L"File Size (BYTES): ");
		OutputDebugStringA(ss.str().c_str());
		OutputDebugStringW(L" Whilst grid expects: ");
		OutputDebugStringA(ss2.str().c_str());
		OutputDebugStringW(L" (BYTES) of data.\n");

		//Guess at the dimentions it should be...
		float dimentionGuess = sqrt((float)fsize);
		ss3 << dimentionGuess;
		OutputDebugStringW(L"Suggested Dimentions (Num verts): ");
		OutputDebugStringA(ss3.str().c_str());
		OutputDebugStringW(L" * ");
		OutputDebugStringA(ss3.str().c_str());
		OutputDebugStringW(L"\n");

		//Inf loop. 
		while(1){}
	}

	// Open the file.
	std::ifstream inFile;
	inFile.open(filename, std::ios_base::binary);

	if(inFile)
	{
		// Read the RAW bytes.
		inFile.read((char*)&in[0], (std::streamsize)in.size()*sizeof(unsigned char));

		// Done with file.
		inFile.close();
	}
	else
	{
		OutputDebugStringW(L"Terrain Patch Error - Could not open RAW file\n");
		while(1){}
	}

	// Copy the array data into a float array and scale it.
	for(UINT i = 0; i < terrainWidth * terrainDepth; i++)
		heightsOut.push_back(((float)in[i]));
}

void TerrainPatch::LoadHeightMapRAW16(ID3D11Device* device, std::vector<float>&heightsOut, 
		const char* filename)
{
	OutputDebugStringW(L"Terrain Error - 16 bit not yet supported...\n");
	while(1){}
}

void TerrainPatch::LoadHeightMapPNG(ID3D11Device* device, std::vector<float>&heightsOut, 
	const char* filename)
{
	//Use the third part lodepng library to load a 16bit PNG - Not my fault
	//regarding the spelling...
	//
	//Will be filled with per pixel data.
	std::vector<unsigned char>image;
	//Unsigned ints representing the size of the file (pixels)
	unsigned fileWidth, fileHeight;

	//Decode to extract the per pixel informaton. 
	unsigned error = lodepng::decode(image, fileWidth, fileHeight, filename, LCT_GREY, 16);

	if (error)
	{
		OutputDebugStringW(L"Terrain Patch Error - Failed to decode png.\n");
		while(1){}
	}

	//Store the terrain dimentions
	terrainWidth = (float)fileWidth;
	terrainDepth = (float)fileHeight;

	//We have 16bit uint data (well... 2 8 bit chars per
	//pixel) - convert to a floating point value (32bit)
	//and fillout our heightsOut vector. 
	std::vector<float>heightTemp;
	for(unsigned i=0; i<terrainWidth*terrainDepth*2; i+=2)
	{
		//2 chars per pixel. We need to merge the two together to get
		//one 16 bit uint value.
		unsigned char first = image[i];
		unsigned char second = image[i+1];

		//Height for this pixel. 
		char16_t height = (char16_t)first << 8 | second;

		//Add to vector.
		heightTemp.push_back((float)height);
	}

	//Our heightsOut vector is odd and requires a specific order - the default
	//way doesnt work correctly... so we have to hack a solution together. 
	for(int j=(int)terrainDepth-1; j>=0; j--)
	{
		for(int i=0; i<(int)terrainWidth; i++)
		{
			UINT index = ((UINT)terrainDepth * j) + i;
			heightsOut.push_back(heightTemp[index]);
		}
	}
}

void TerrainPatch::ScaleHeightMap(std::vector<float>&heights, float factor)
{
	//Dont divide by 0 or 1. Former breaks things, latter does nothing. 
	if (factor == 0.0f || factor == 1.0f)
		return;

	for(unsigned i = 0; i < heights.size(); i++)
		heights[i] /= factor;

}

void TerrainPatch::CreateGrid(ID3D11Device* device, std::vector<float>&heights, 
		float minTexCoordX, float minTexCoordY, 
		float texCoordStepX, float texCoordStepY)
{
	GeometryGenerator geoGen;
	GeometryGenerator::MeshData data;

	//Create grid. 
	geoGen.CreateGrid(terrainWidth * terrainCellSize, terrainDepth * terrainCellSize, 
		(UINT)terrainDepth, (UINT)terrainWidth, data);

	//Extract VB Data
	std::vector<TerrainFX::PerVertex>verts(data.Vertices.size());
	for (unsigned i = 0; i < data.Vertices.size(); i++)
	{
		//Work out position - displace later.
		XMFLOAT3 p = data.Vertices[i].Position;

		//Extract required VB data
		verts[i].PosL = p;
		verts[i].NormalL = data.Vertices[i].Normal;
		verts[i].TangentL = data.Vertices[i].TangentU;
		verts[i].Tex = data.Vertices[i].TexC;
	}

	//Displace each vertex Y coord - trial and error got this result :) 
	//
	//Where to index in to the heights vector.
	int sampleIndex = 0;
	for(int j=(int)terrainDepth-1; j>=0; j--)
	{
		for(int i=0; i<(int)terrainWidth; i++)
		{		
			//Which vertex?
			UINT index = ((UINT)terrainDepth * j) + i;
			//Displace
			verts[index].PosL.y = heights[sampleIndex];

			//Store the position to our system copy of positions - used when creating AABB
			//and returning the height at (x,z)
			sysCopyVerts.push_back(verts[index].PosL);

			//Increment the location we sample in to heights. 
			sampleIndex++;

			//Calculate the layer texture coordinate.
			XMFLOAT2 layerTexC = XMFLOAT2(minTexCoordX + (i*texCoordStepX),
				minTexCoordY + (j*texCoordStepY));
			verts[index].TexLayer = layerTexC;
		}
	}

	//Generate normals
	GenerateNormals(verts, data.Indices);
	//Generate tangent vectors
	GenerateTangents(verts, data.Indices);

	//Create VB. 
	D3D11_BUFFER_DESC bd;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.ByteWidth = sizeof(TerrainFX::PerVertex) * data.Vertices.size();
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	bd.StructureByteStride = 0;
	bd.Usage = D3D11_USAGE_IMMUTABLE;

	D3D11_SUBRESOURCE_DATA sd;
	sd.pSysMem = &verts[0];

	//Create buffer in GPU memory
	HR(device->CreateBuffer(&bd, &sd, &terrainLayerVB));

	//Create IB. 
	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bd.ByteWidth = sizeof(UINT) * data.Indices.size();

	sd.pSysMem = &data.Indices[0];

	//Create IB in GPU memory. 
	HR(device->CreateBuffer(&bd, &sd, &terrainLayerIB));

	//Store index count 
	terrainLayerDrawCount = data.Indices.size();
}

void TerrainPatch::GenerateNormals(std::vector<TerrainFX::PerVertex>&verts, 
	std::vector<UINT>&inds)
{
	//Assertions. 
	assert(inds.size() != 0);
	assert(verts.size() != 0);

	//Number of faces in this grid. 
	UINT numFaces = inds.size()/3;

	//Vector of face normals. 
	std::vector<XMFLOAT3>faceNormals(numFaces);

	//For every face in the mesh, calculate their normal vector. 
	for (UINT i = 0; i < numFaces; i++)
	{
		//Where to index in to verts to recive the locations of each face
		UINT index1, index2, index3;
		//Locations of each vertex. 
		XMFLOAT3 vertex1, vertex2, vertex3;
		//Two vectors for each face. 
		XMFLOAT3 vectorXM1, vectorXM2;
		XMVECTOR vector1, vector2;

		//Workout where to index in to the verts vector. 
		index1 = inds[i*3+0];
		index2 = inds[i*3+1];
		index3 = inds[i*3+2];

		//Extract the locations. 
		vertex1 = verts[index1].PosL;
		vertex2 = verts[index2].PosL;
		vertex3 = verts[index3].PosL;

		//Calculate the two vectors for this face
		vectorXM1.x = vertex1.x - vertex3.x;
		vectorXM1.y = vertex1.y - vertex3.y;
		vectorXM1.z = vertex1.z - vertex3.z;
		vectorXM2.x = vertex3.x - vertex2.x;
		vectorXM2.y = vertex3.y - vertex2.y;
		vectorXM2.z = vertex3.z - vertex2.z;

		//Store in to XMVECTOR type.
		vector1 = XMLoadFloat3(&vectorXM1);
		vector2 = XMLoadFloat3(&vectorXM2);

		//Calculate the normlized normal vector for this face. 
		XMVECTOR fNormal = XMVector3Cross(vector2, vector1);
		fNormal = XMVector3Normalize(fNormal);
		XMStoreFloat3(&faceNormals[i], fNormal);

		//Each vertex that made up this face shares the same normal
		//vector. 
		verts[index1].NormalL = faceNormals[i];
		verts[index2].NormalL = faceNormals[i];		
		verts[index3].NormalL = faceNormals[i];
	}
}

void TerrainPatch::GenerateTangents(std::vector<TerrainFX::PerVertex>&verts, 
	std::vector<UINT>&inds)
{
	XMFLOAT3 *tan1 = new XMFLOAT3[verts.size() * 2];
	XMFLOAT3 *tan2 = tan1+verts.size();
	ZeroMemory(tan1, verts.size() * sizeof(XMFLOAT3) * 2);

	for (long a = 0; a < (long)inds.size()/3; a++)
	{
		//Indicies for ath trinalge
		long i1 = inds[a*3+0];
		long i2 = inds[a*3+1];
		long i3 = inds[a*3+2];

		//Extract verticies and texcoords
		XMFLOAT3 v1 = verts[i1].PosL;
		XMFLOAT3 v2 = verts[i2].PosL;
		XMFLOAT3 v3 = verts[i3].PosL;

		XMFLOAT2 w1 = verts[i1].Tex;
		XMFLOAT2 w2 = verts[i2].Tex;
		XMFLOAT2 w3 = verts[i3].Tex;

		float x1 = v2.x - v1.x;
		float x2 = v3.x - v1.x;
		float y1 = v2.y - v1.y;
		float y2 = v3.y - v1.y;
		float z1 = v2.z - v1.z;
		float z2 = v3.z - v1.z;

		float s1 = w2.x - w1.x;
		float s2 = w3.x - w1.x;
		float t1 = w2.y - w1.y;
		float t2 = w3.y - w1.y;

		float r = 1.0f / (s1 * t2 - s2 * t1);

		XMFLOAT3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, 
			(t2 * z1 - t1 * z2) * r);

		XMFLOAT3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, 
			(s1 * z2 - s2 * z1) * r);

		tan1[i1] = XMFLOAT3Add(tan1[i1], sdir);
		tan1[i2] = XMFLOAT3Add(tan1[i2], sdir);
		tan1[i3] = XMFLOAT3Add(tan1[i3], sdir);

		tan2[i1] = XMFLOAT3Add(tan2[i1], tdir);
		tan2[i2] = XMFLOAT3Add(tan2[i2], tdir);
		tan2[i3] = XMFLOAT3Add(tan2[i3], tdir);
	}

	for (long a = 0; a < (long)verts.size(); a++)
	{
		XMFLOAT3 n = verts[a].NormalL;
		XMFLOAT3 t = tan1[a];
		
		XMVECTOR nV = XMLoadFloat3(&n);
		XMVECTOR tV = XMLoadFloat3(&t);

		//Gram-Schmidth Orthogonalize
		XMVECTOR v = (tV - nV * XMVector3Dot(nV, tV));
		v = XMVector3Normalize(v);

		//store
		XMStoreFloat3(&verts[a].TangentL, v);
	}

	//Clean up array
	delete[] tan1;
}

void TerrainPatch::GenerateMedQualityTerrain(ID3D11Device* device, 
	std::vector<float>&heights)
{
	GeometryGenerator geoGen;
	GeometryGenerator::MeshData data;

	//Create grid - Same with and depth (World space units), but with half the number
	//of verticies (plus 1) in X and Z direction (Thus, this patch will have 1/4th the number of verts 
	//of the fully tessellated patch)
	//
	//Therefore, with a patch size of 128*128, the med pach will be 64+1*64+1 verts but
	//128*128 in world space units. 
	geoGen.CreateGrid(terrainWidth * terrainCellSize, terrainDepth * terrainCellSize,
		(UINT)((terrainDepth/2.0f)), (UINT)((terrainWidth/2.0f)), data);

	//Extract VB Data
	std::vector<TerrainFX::PerVertex>verts(data.Vertices.size());
	for (unsigned i = 0; i < data.Vertices.size(); i++)
	{
		//Work out position - displace later.
		XMFLOAT3 p = data.Vertices[i].Position;

		//Extract required VB data
		verts[i].PosL = p;
		verts[i].NormalL = data.Vertices[i].Normal;
		verts[i].TangentL = data.Vertices[i].TangentU;
		verts[i].Tex = data.Vertices[i].TexC;
	}

	//Displace each vertex Y coord - trial and error got this result :) 
	//
	//Where to index in to the heights vector.
	int sampleIndex = 0;
	for(int j=(int)(terrainDepth/2.0f)-1; j>=0; j--)
	{
		for(int i=0; i<(int)(terrainWidth/2.0f); i++)
		{		
			//Which vertex?
			UINT index = ((UINT)(terrainDepth/2.0f) * j) + i;
			//Displace
			verts[index].PosL.y = heights[sampleIndex];

			//Increment the location we sample in to heights. 
			sampleIndex++;
		}
	}

	//Generate normals
	GenerateNormals(verts, data.Indices);
	//Generate tangent vectors
	GenerateTangents(verts, data.Indices);

	//Create VB. 
	D3D11_BUFFER_DESC bd;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.ByteWidth = sizeof(TerrainFX::PerVertex) * data.Vertices.size();
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	bd.StructureByteStride = 0;
	bd.Usage = D3D11_USAGE_IMMUTABLE;

	D3D11_SUBRESOURCE_DATA sd;
	sd.pSysMem = &verts[0];

	//Create buffer in GPU memory
	HR(device->CreateBuffer(&bd, &sd, &terrainLayerMedVB));

	//Create IB. 
	bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bd.ByteWidth = sizeof(UINT) * data.Indices.size();

	sd.pSysMem = &data.Indices[0];

	//Create IB in GPU memory. 
	HR(device->CreateBuffer(&bd, &sd, &terrainLayerMedIB));

	//Store index count 
	terrainLayerMedDrawCount = data.Indices.size();
}

void TerrainPatch::Render(bool isZPrePass, bool isOpaquePass, bool isShadowPass,		 
		VirtualCamera* playerCamera,										   
		DirecLightShadowMapData* shadowMap,									    
		SSAOData* ssaoMap,													   
		ID3D11ShaderResourceView* envMap,									    
		ID3D11ShaderResourceView* llb,                                          
		ID3D11ShaderResourceView* llibO, ID3D11ShaderResourceView* llsebO,      
		ID3D11ShaderResourceView* llibT, ID3D11ShaderResourceView* llsebT,     
		DirectionalLight* dLightArr, unsigned dLightCount,					    
		XMFLOAT3 &hemiColDown, XMFLOAT3 &hemiColRange,						  
		CXMMATRIX &view, CXMMATRIX &proj,									   
		ID3D11DeviceContext* context)
{
	//Get the terrain effect
	static TerrainFX* terrainEffect = NULL;
	terrainEffect = ShaderManager::GetInstance()->ReturnTerrainFX();

	//Terrain is an opaque piece of geometry - only render when we should.  
	if ( (isOpaquePass) && (isInit) )
	{
		//Set the correct technique depending. Note: Not a shadow caster
		//just yet. 
		if (isShadowPass)
			return;
		else if (isZPrePass)
			terrainEffect->SetActiveTechniqueByName("ZPrePass");
		else
		{
			if (useSlopeBasedRendering)
				terrainEffect->SetActiveTechniqueByName("Terrain");
			else
				terrainEffect->SetActiveTechniqueByName("Terrain_Height");
		}

		//Ensure we have some tex data
		assert(terrainTexData != 0);

		//High or low quality?
		//
		//Vector between camera and the patch center.
		XMFLOAT3 vec;
		vec.x = playerCamera->GetPosition().x - position.x;
		vec.y = 0.0f; //XZ Plane only. 
		vec.z = playerCamera->GetPosition().z - position.z;
		XMVECTOR distV = XMVectorSet(vec.x, vec.y, vec.z, 0.0f);
		XMVECTOR lenghtV = XMVector3Length(distV);
		XMFLOAT3 lengthF;
		XMStoreFloat3(&lengthF, lenghtV);
		float dist = lengthF.x;

		//Decide what VB & IB to bind. Also decide on how many inds to
		//draw
		ID3D11Buffer* vb = 0;
		ID3D11Buffer* ib = 0;
		unsigned drawCount = 0;

		//High quality
		if ( (dist < TERRAIN_PATCH_MED_QUALITY_RANGE) || (terrainLayerMedVB == 0) )
		{
			vb = terrainLayerVB;
			ib = terrainLayerIB;
			drawCount = terrainLayerDrawCount;
		}
		//Low Quality
		else
		{
			vb = terrainLayerMedVB;
			ib = terrainLayerMedIB;
			drawCount = terrainLayerMedDrawCount;
		}

		//Render
		context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
		context->IASetInputLayout(terrainEffect->ReturnInputLayout());

		UINT stride = sizeof(TerrainFX::PerVertex);
		UINT offset = 0;
		context->IASetVertexBuffers(0, 1, &vb, &stride, &offset);
		context->IASetIndexBuffer(ib, DXGI_FORMAT_R32_UINT, 0);

		//Work out global world matrix
		XMMATRIX wd = XMLoadFloat4x4(&worldMatrix);
		XMMATRIX wit = MathHelper::InverseTranspose(wd);
		XMMATRIX wvp = (wd * view) * proj;
		XMMATRIX wv = wd * view;
		//SSAO Transform. 
		XMMATRIX ssaoTrans = wvp * TO_TEX_SPACE;

		//Set variables.
		terrainEffect->SetTextureMap(
			terrainTexData->DiffuseMap0, 
			terrainTexData->DetailMap0, 
			terrainTexData->NormalMap0, 
			terrainTexData->DiffuseMap1, 
			terrainTexData->NormalMap1, 
			terrainTexData->DiffuseMap2, 
			terrainTexData->NormalMap2, 
			terrainTexData->HeightMap0,
			terrainTexData->HeightMap1,
			terrainTexData->HeightMap2, 
			terrainTexData->SpecMap0,
			terrainTexData->SpecMap1,
			terrainTexData->SpecMap2); 

		//Set layer data
		terrainEffect->SetLayerTexData(terrainTexData->LayerMap1, 
			terrainTexData->Layer1Tex, terrainTexData->Layer1Normal, 
			terrainTexData->Layer1Spec, terrainTexData->Layer1Height);

		//Controll these by the #defines in the header. 
		terrainEffect->SetPOMData(TERRAIN_POM_MIN_SAMPLES, TERRAIN_POM_MAX_SAMPLES, 
			TERRAIN_POM_HEIGHT_0_SCALE, TERRAIN_POM_HEIGHT_1_SCALE, TERRAIN_POM_HEIGHT_2_SCALE);

		terrainEffect->SetLightingData(playerCamera->GetPosition(), dLightArr, dLightCount, llibO, llsebO); //Opaque
		terrainEffect->SetHemisphericalData(hemiColDown, hemiColRange);
		terrainEffect->SetSSAOData(ssaoMap->ReturnSSAOMap(), ssaoTrans);

		if ((!isShadowPass) && (shadowMap))
		{
			terrainEffect->SetShadowMap(shadowMap->GetShadowMapSRV());
			terrainEffect->SetShadowMapMatricies(shadowMap->GetLightView(), shadowMap->GetLightProj(), shadowMap->GetNDCToTex());
		}
		else
		{
			XMMATRIX i = XMMatrixIdentity();
			terrainEffect->SetShadowMap(0);
			terrainEffect->SetShadowMapMatricies(i, i, i);
		}

		//Per Object CB
		terrainEffect->SetCBufferPerObject(wd,
			wit, 
			wvp, 
			terrainMaterial,
			wv);
		
		//Tex Teansforms
		terrainEffect->SetTexTransforms(XMLoadFloat4x4(&terrainTexTrans), 
			XMLoadFloat4x4(&terrainTexDetailTrans));

		//if this is not slope based (height based), then set the height data
		if (!useSlopeBasedRendering)
			terrainEffect->SetHeightBasedPatchValues(heightFac1, heightFac2);

		//Draw
		for (unsigned p = 0; p < terrainEffect->ReturnActiveTechniqueNumberOfPasses(); p++)
		{
			terrainEffect->ApplyActiveTechniqueForPass(p, context);
			context->DrawIndexed(drawCount, 0, 0);
		}

		//Unbind resources.
		ID3D11ShaderResourceView* srvNULL[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
		context->PSSetShaderResources(0, 23, srvNULL);
	}

	//Reset RS state (incase its changed to wireframe)
	//context->RSSetState(NULL);
}